package com.fanrui.code.db.jvmdb;

import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.ibatis.session.SqlSession;

public abstract class JvmDao<T extends IPersistable<ID_TYPE>,ID_TYPE> {
	
	protected enum Modify {
		SAVE,
		DELETE
	}
	
	/**
	 * 需要被保存或删除的实体
	 */
	protected ConcurrentMap<T, Modify> modifiedEntities = new ConcurrentHashMap<T, Modify>();
	
	
	/**
	 * 所有活跃的实体
	 */
	protected ConcurrentMap<ID_TYPE, T> entities = new ConcurrentHashMap<ID_TYPE, T>();
	
	/**
	 * 实体
	 */
	protected Class<T> entityClass;
	
	protected JvmDao(Class<T> entityClass) {
		this.entityClass = entityClass;
	}
	
	public Collection<T> findAll() {
		return this.entities.values();
	}
	
	/**
	 * 加载所有活跃的实体到JVM里。（一般在服务器启动时调用） 。
	 */
	public void loadAll() {
		for(T entity : dbFindAll()) {
			// 保存到 jvm
			jvmSave(entity);
		}
	}
	
	/**
	 * 加载所有活跃的实体到JVM里。（一般在服务器启动时调用） 。
	 */
	public void loadAll(SqlSession session) {
		for(T entity : dbFindAll(session)) {
			jvmSave(entity);
		}
	}
	
	/**
	 * 保存实体（定期保存到db）
	 * @param entity
	 */
	public void save(T entity) {
		// 只是做标记，以便后续SaveLoop中执行保存。
		this.modifiedEntities.putIfAbsent(entity, Modify.SAVE);
	}
	
	/**
	 * 立即保存实体
	 * @param entity
	 */
	public void saveImmediately(T entity) {
		// 清理掉定期保存标志。
		this.modifiedEntities.remove(entity);
		// 通知db服务器保存数据  使用同步的方式保存 ,保存成功后才能进行后续操作
		dbSave(entity);
	}
	
	/**
	 * 删除实体
	 * @param id
	 */
	public void delete(ID_TYPE id) {
		// 先从JVM中删除。
		T entity = delete4Jvm(id);
		// 然后做标记，以便后续SaveLoop中执行删除。
		this.modifiedEntities.putIfAbsent(entity, Modify.DELETE);
	}
	
	/**
	 * 实现立即删除（相对于上面的定时保存的delete()来说）
	 * @param entityId
	 */
	public void deleteImmediately(ID_TYPE id) {
		// 先从JVM中删除。
		T entity = delete4Jvm(id);
		// 清理掉定期删除标志。
		this.modifiedEntities.remove(entity);
		// 让SaveLoop立即执行。
		dbDelete(id);
	}

	/**
	 * 根据id查找实体
	 * @param id
	 * @return
	 */
	public T findById(ID_TYPE id) {
		return this.entities.get(id);
	}
	
	/**
	 * 新建一个Entity（已经有EntityID，并且放入了JVM，做了Save标记）
	 */
	public T create() {
		T entity = null;
		try {
			entity = entityClass.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
		jvmSave(entity);
		save(entity);
		return entity;
	}
	
	public T create(ID_TYPE id) {
		T entity = null;
		try {
			entity = entityClass.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
		entity.setPersistKey(id);
		jvmSave(entity);
		save(entity);
		return entity;
	}
	
	/**
	 * 把实体放入JVM内存
	 * @param entity
	 */
	protected void jvmSave(T entity) {
		T oldEntity = this.entities.putIfAbsent(entity.getPersistKey(), entity);
		if(null != oldEntity) {
			throw new IllegalArgumentException(String.format("%d 对应的 实体已经存在！%s", entity.getPersistKey(), entity));
		}
		// 再放到各自个性化的数据结构中去（比如：按Name索引的Map）
		OtherSave4jvm(entity);
	}
	
	/**
	 * 从JVM内存中删除实体
	 * @param id
	 * @return
	 */
	private T delete4Jvm(ID_TYPE id) {
		// 先从Map<EntityId, Entity>中，删除。
		T entity = this.entities.remove(id);
		// 然后从其他个性化的数据结构中，删除。
		otherDelete4Jvm(entity);
		return entity;
	}

	/**
	 * 子类应覆盖此方法。在其他个性化的数据结构中保持住Entity
	 * @param entity
	 */
	protected abstract void OtherSave4jvm(T entity);

	/**
	 * 子类需要实现该方法，自己从数据库查找所有活跃的实体。
	 * @return
	 */
	protected abstract Collection<T> dbFindAll();
	
	/**
	 * 子类需要实现此方法。从数据库查找所有活跃的Entity
	 * @param session
	 * @return
	 */
	protected Collection<T> dbFindAll(SqlSession session) {
		return null;
	}
	
	/**
	 * 通过持久层保存对象
	 * @param entity
	 */
	protected abstract void dbSave(T entity);
	
	/**
	 * 子类应覆盖此方法。在这里执行从其他个性化的数据结构中删除Entity的操作。
	 * 
	 * @param id
	 */
	protected abstract void otherDelete4Jvm(T entity);
	
	/**
	 * 持久层删除对象
	 * @param id
	 */
	protected abstract void dbDelete(ID_TYPE id);

}
